package com.fjsh.queues;

import com.fjsh.consts.ExchangeConst;
import com.fjsh.consts.QueuesConst;
import com.fjsh.consts.RoutingKeyConst;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DirectDeadLetterQueue {

    //region 死信队列
    /**
     * 所谓死信：即（1）消息被拒绝（basic.reject 或者 basic.nack），并且requeue=false;（2）消息的过期时间到期了；
     * （3）队列长度限制超过了 等三个因素造成。
     * 我们会将以上原因造成的队列存入死信队列，死信队列其实也是一个普通的队列，我们可以根据自身需要，可以对死信进行操作。
     * 以下为死信队列的演示（将正常队列监听关闭并设置超时）：首先声明一个正常的队列，并设置死信队列的相关声明【死信交换器（与正常队列一致即可），死信路由Key等】
     * 设置完后，准备一个新的队列，此队列用于接收上一个正常队列发生死信后，将由此队列代替（即候补队列），然后将新队列通过上一个交换器以及正常队列中声明的死信路由Key进行绑定
     * 该操作与正常声明一致（声明交换器（可使用正常队列的交换器，无需另外声明），队列，将队列绑定到交换器）
     */

    /**
     * 声明交换器（此处正常的与死信的交换器一致），原因是队列因延迟或者其他原因未被消费造成了超时了，通过死信继续路由到该队列等待消费
     */
    @Bean
    public Exchange testDeadExchange() {
        return new DirectExchange(ExchangeConst.TEST_DEAD_EXCHANGE, true, false);
    }

    /**
     * 声明一个正常的队列，并设置死信相关信息（交换器，路由Key），确保发生死信后会将死信存入交换器
     */
    @Bean
    public Queue testQueue() {
        Map<String, Object> args = new HashMap<>(16);
        // x-dead-letter-exchange 声明 死信交换机
        args.put("x-dead-letter-exchange", ExchangeConst.TEST_DEAD_EXCHANGE);
        // x-dead-letter-routing-key 声明死信路由键
        args.put("x-dead-letter-routing-key", RoutingKeyConst.TEST_DEAD_ROUTINGKEY);

        // 继续使用直连队列
        return new Queue(QueuesConst.TEST_DIRECT_DEAD_QUEUE, true, false, false, args);
    }

    /**
     * 将队列绑定到指定交换器并设置路由
     */
    @Bean
    public Binding testBinding() {
        return BindingBuilder.bind(testQueue()).to(testDeadExchange()).with(RoutingKeyConst.TEST_DIRECT_ROUTINGKEY).noargs();
    }

    /**
     * 死信队列（候补队列） 若上面的正常队列发生死信时，需将发生死信的队列信息路由到此队列中
     * 路由过程：正常队列发送->信息到交换器->交换器路由到正常队列->监听,发生死信->死信回到指定的交换器->再由交换器路由到死信队列->死信监听
     */
    @Bean
    public Queue testDeadQueue() {
        return new Queue(QueuesConst.TEST_DEAD_QUEUE, true, false, false);
    }

    /**
     * 绑定死信的队列到候补队列
     */
    @Bean
    public Binding testDeadBinding() {
        return BindingBuilder.bind(testDeadQueue()).to(testDeadExchange()).with(RoutingKeyConst.TEST_DEAD_ROUTINGKEY).noargs();
    }
    //endregion
}
